package weka.classifiers.functions._HC_m3;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Vector;

import weka.core.Instance;
import weka.core.Instances;

/**
 * 
 * Divide data by parallel the hyperplane (1,1,1,...,1)
 * 
 * @author huicongwm
 * 
 */
public class _HC_HyperPlaneDivider extends _HC_RandomDivider {

	protected class SortStruct implements Comparable, Serializable {
		public int idx;

		public double dist;

		public SortStruct(int i, double d) {
			idx = i;
			dist = d;
		}

		public int compareTo(Object arg0) {
			SortStruct st = (SortStruct) arg0;
			if (dist < st.dist) {
				return -1;
			}
			if (dist > st.dist) {
				return 1;
			}
			return 0;
		}
	}

	/**
	 * The max dimension of the instances
	 */
	protected int m_Dim;

	/**
	 * sqrt(m_Dim), ||w||, w=(1,1,...,1)
	 */
	protected double m_Mod;

	protected HashMap<Double, List<SortStruct>> m_InvIndex = new HashMap<Double, List<SortStruct>>();

	public void buildDivider(Instances _instances) {
		super.buildDivider(_instances);
		m_Dim = _instances.numAttributes();
		m_Mod = Math.sqrt((double) m_Dim);
		for (double k : m_LabelCount.keySet()) {
			List<SortStruct> st = new ArrayList<SortStruct>();
			m_InvIndex.put(k, st);
		}
		for (int i = 0; i < m_Instances.numInstances(); ++i) {
			double c = m_Instances.instance(i).classValue();
			m_InvIndex.get(c).add(
					new SortStruct(i, calcDist(m_Instances.instance(i))));
		}
		for (double k : m_LabelCount.keySet()) {
			Collections.sort(m_InvIndex.get(k));
		}
	}

	@Override
	public Instances[] divide(double _classValue) {
		int base = m_LabelBase.get(_classValue);
		int up = m_LabelUpper.get(_classValue);
		Instances[] ret = new Instances[base];
		for (int i = 0; i < base; ++i) {
			ret[i] = new Instances(m_Instances, 0);
		}
		List<SortStruct> cidx = m_InvIndex.get(_classValue);
		int cur = 0;

		for (int i = 0; i < cidx.size(); ++i) {
			if (ret[cur].numInstances() + 1 > up) {
				++cur;
			}
			ret[cur].add(m_Instances.instance(cidx.get(i).idx));
		}

		return ret;
	}

	private double calcDist(Instance instance) {
		double s = 1;
		for (int i = 0; i < instance.numAttributes(); ++i) {
			s += instance.value(i);
		}
		return s / m_Mod;
	}

}
